module afifo
#(
        parameter     DATA_SIZE         =       32      ,
        parameter     ADDR_SIZE         =       3
);
(
        input   wire                    clk_i           ,
        input   wire                    rstn_i          ,
        input   wire                    push            ,
        input   wire  [DATA_SIZE-1:0]   DATA_IN         ,
        output  wire                    fifo_full       ,

        input   wire                    clk_o           ,
        input   wire                    rstn_o          ,
        input   wire                    pop             ,
        output  wire  [DATA_SIZE-1]     DATA_OUT        ,
        output  wire                    fifo_empty
);

parameter             DATA_DEPTH = 1<<ADDR_SIZE         ;
reg     [DATA_SIZE-1:0]  FIFO_MEM  [DATA_DEPTH-1 : 0 ]  ;

wire                  [ADDR_SIZE-1:0]   wr_addr         ;
wire                  [ADDR_SIZE-1:0]   rd_addr         ;

reg                   [ADDR_SIZE : 0]   wrpt            ;
reg                   [ADDR_SIZE : 0]   rdpt            ;

wire                  [ADDR_SIZE : 0]   wrpt_gray       ;
reg                   [ADDR_SIZE : 0]   wrpt_gray_d1    ;
reg                   [ADDR_SIZE : 0]   wrpt_gray_d2    ;
wire                  [ADDR_SIZE : 0]   rdpt_gray       ;
reg                   [ADDR_SIZE : 0]   rdpt_gray_d1    ;
reg                   [ADDR_SIZE : 0]   rdpt_gray_d2    ;

//===== FIFO memory initial and write =====//

genvar i;
generate 
for (i = 0 ; i < DATA_DEPTH ; i=i+1) begin   : fifo_init
        always@(posedge clk_i or rstn_i)begin
                if(rstn_i)
                        FIFO_MEM[i]             <= 32'h0                ;
                else if(push & (~fifo_full))
                        FIFO_MEM[wr_addr]       <= DATA_IN              ;
                else
                        FIFO_MEM[wr_addr]       <= FIFO_MEM[wr_addr]    ;
        end
end
endgenerate

//=========== FIFO read memory ============//
always@(posedge clk_i or rstn_o )begin
        if(rstn_o)      
                DATA_OUT <= 32'h0               ;
        else if(pop & (~fifo_empty))
                DATA_OUT <= FIFO_MEM[rd_addr]   ;
        else 
                DATA_OUT <= 32'h0               ;
end

//=========== addr calc ===================//
assign wr_addr = wrpt[ADDR_SIZE-1-:ADDR_SIZE];
assign rd_addr = rdpt[ADDR_SIZE-1-:ADDR_SIZE];



//===============gray code sync============//
        //----------write-------//
always@(posedge clk_i or rstn_i)begin
        if(rstn_i) begin
                wrpt_gray_d1 <= ADDR_SIZE'h0 ;
                wrpt_gray_d2 <= ADDR_SIZE'h0 ;
        end
        else    
                wrpt_gray_d1 <= wrpt_gray    ;
                wrpt_gray_d2 <= wrpt_gray_d1 ;
        end               
end
always@(posedge clk_i or rstn_i)begin
        if(rstn_i)
                wrpt         <= ADDR_SIZE'h0 ;
        else if(push & (~fifo_full))    
                wrpt         <= wrpt + 1'b1  ;
        else    
                wrpt         <= wrpt         ;
end

        //----------read--------//
always@(posedge clk_o or rstn_o)begin
        if(!rstn_o) begin
                rdpt_gray_d1 <= ADDR_SIZE'h0 ;
                rdpt_gray_d2 <= ADDR_SIZE'h0 ;
        end
        else begin
                rdpt_gray_d1 <= rdpt_gray    ;
                rdpt_gray_d2 <= rdpt_gray_d1 ;
        end
end
always@(posedge clk_o or rstn_o)begin
        if(rstn_o)begin
                rdpt         <= ADDR_SIZE'h0 ;
        else if(pop & (~fifo_empty))
                rdpt         <= rdpt + 1'b1  ;
        else
                rdpt         <= rdpt         ;
        end
end

//=========== Gray Code calc  =============//

assign wrpt_gray = (wrpt >> 1) ^ wrpt ;
assign rdpt_gray = (rdpt >> 1) ^ rdpt ;

assign fifo_full = (wrpt_gray  == {~(rdpt_gray_d2[ADDR_SIZE-:2]),rdpt_gray_d2[ADDR_SIZE-2:0]} ；
assign fifo_empty= wrpt_gray_d2 == rdpt_gray ;


endmodule
